package com.sayice.robotconflict.game.object;

import cn.hutool.core.bean.BeanUtil;
import com.sayice.robotconflict.game.Game;
import com.sayice.robotconflict.game.GameMatcher;
import com.sayice.robotconflict.game.msg.Message;
import com.sayice.robotconflict.game.msg.data.send.TeamPlayerInfo;
import com.sayice.robotconflict.game.object.Player.State;
import io.netty.channel.ChannelFutureListener;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;

/**
 * 组队房间
 *
 * @author shubing
 */
public class TeamRoom {
    /**
     * 房间号
     */
    private final int id;
    
    /**
     * 玩家容量
     */
    private final int maxSize;
    
    /**
     * 玩家人数
     */
    private final AtomicInteger size = new AtomicInteger();
    
    /**
     * 已准备的玩家人数
     */
    private final AtomicInteger readyCount = new AtomicInteger();
    
    /**
     * 玩家，value表示是否已准备
     */
    private final Map<Player, Boolean> players;
    
    /**
     * 玩家下线监听器
     */
    private final ChannelFutureListener offlineListener;
    
    /**
     * 房间是否已关闭
     */
    private boolean closed;
    
    /**
     * 房间状态 (closed) 读写锁
     */
    private final ReadWriteLock stateReadWriteLock = new ReentrantReadWriteLock();
    
    /**
     * 房间状态 (closed) 读锁
     */
    private final Lock stateReadLock = stateReadWriteLock.readLock();
    
    /**
     * 房间状态 (closed) 写锁
     */
    private final Lock stateWriteLock = stateReadWriteLock.writeLock();
    
    private TeamRoom(int roomId, int maxSize) {
        this.id = roomId;
        this.maxSize = maxSize;
        this.players = new ConcurrentHashMap<>((int) Math.ceil(maxSize / 0.75));
        // 玩家下线时从房间中移除
        offlineListener = future -> {
            for (Player player : players.keySet()) {
                if (player.channelId().equals(future.channel().id())) {
                    removePlayer(player);
                    return;
                }
            }
        };
    }
    
    /**
     * 创建房间
     *
     * @param maxSize     玩家容量
     * @param firstPlayer 第一个玩家
     */
    public static void createRoom(int maxSize, Player firstPlayer) {
        int roomId = Game.MAX_TEAM_ROOM_ID.incrementAndGet();
        TeamRoom teamRoom = new TeamRoom(roomId, maxSize);
        teamRoom.addPlayer(firstPlayer);
        Game.TEAM_ROOM_MAP.put(roomId, teamRoom);
    }
    
    /**
     * 添加玩家
     *
     * @param player 玩家
     * @return 添加成功时为 true，否则为 false
     */
    public boolean addPlayer(Player player) {
        stateReadLock.lock();
        try {
            if (closed) {
                return false;
            }
            if (!player.state.compareAndSet(State.IDLE, State.TEAMING)) {
                return false;
            }
            int currentSize;
            while ((currentSize = size.get()) < maxSize) {
                int newSize = currentSize + 1;
                if (size.compareAndSet(currentSize, newSize)) {
                    players.put(player, false);
                    sendPlayerInfo();
                    player.closeFuture().addListener(offlineListener);
                    return true;
                }
            }
            player.state.compareAndSet(State.TEAMING, State.IDLE);
            return false;
        } finally {
            stateReadLock.unlock();
        }
    }
    
    /**
     * 移除玩家
     *
     * @param player 玩家
     */
    public void removePlayer(Player player) {
        int newSize;
        stateReadLock.lock();
        try {
            if (closed) {
                return;
            }
            // 准备状态下不能移除
            Boolean ready = players.get(player);
            if (ready == null || ready) {
                return;
            }
            if (!player.state.compareAndSet(State.TEAMING, State.IDLE)) {
                return;
            }
            newSize = size.decrementAndGet();
            players.remove(player);
            sendPlayerInfo();
            player.closeFuture().removeListener(offlineListener);
        } finally {
            stateReadLock.unlock();
        }
        // 若所有玩家已退出则关闭房间
        stateWriteLock.lock();
        try {
            if (newSize == 0) {
                close();
            }
        } finally {
            stateWriteLock.unlock();
        }
    }
    
    /**
     * 设置准备状态
     *
     * @param player 玩家
     * @param ready  准备状态，true表示准备，false表示取消准备
     */
    public void setReady(Player player, boolean ready) {
        Boolean currentReady = players.get(player);
        if (currentReady == null || currentReady.equals(ready)) {
            return;
        }
        if (ready) {
            int newReadyCount = readyCount.incrementAndGet();
            players.put(player, true);
            sendPlayerInfo();
            stateWriteLock.lock();
            try {
                if (newReadyCount == size.get()) {
                    // 所有玩家已准备，开始匹配
                    Map<String, Player> playerMap = players.keySet()
                            .stream().peek(it -> {
                                it.state.compareAndSet(State.TEAMING, State.IDLE);
                                it.closeFuture().removeListener(offlineListener);
                            })
                            .collect(Collectors.toMap(it -> it.id, it -> it, (a, b) -> b));
                    GameMatcher.addMatch(new Team(playerMap), maxSize);
                    // 关闭房间
                    close();
                }
            } finally {
                stateWriteLock.unlock();
            }
        } else {
            readyCount.decrementAndGet();
            players.put(player, false);
            sendPlayerInfo();
        }
    }
    
    /**
     * 向房间中的玩家发送所有玩家信息
     */
    private void sendPlayerInfo() {
        List<TeamPlayerInfo> teamPlayerInfos = players.entrySet().stream().map(entry -> {
            TeamPlayerInfo teamPlayerInfo = BeanUtil.copyProperties(entry.getKey(), TeamPlayerInfo.class);
            teamPlayerInfo.setRoomId(id);
            teamPlayerInfo.setReady(entry.getValue());
            return teamPlayerInfo;
        }).collect(Collectors.toList());
        while (teamPlayerInfos.size() < maxSize) {
            teamPlayerInfos.add(null);
        }
        players.keySet().forEach(player -> player.send(new Message("team", "players", teamPlayerInfos)));
    }
    
    /**
     * 关闭房间
     */
    private void close() {
        closed = true;
        Game.TEAM_ROOM_MAP.remove(id);
        // 回收房间号
        int maxRoomId = Game.MAX_TEAM_ROOM_ID.get();
        if (Game.TEAM_ROOM_MAP.isEmpty()) {
            Game.MAX_TEAM_ROOM_ID.compareAndSet(maxRoomId, 0);
        } else if (id == maxRoomId) {
            Game.MAX_TEAM_ROOM_ID.compareAndSet(maxRoomId, maxRoomId - 1);
        }
    }
}
